Skip to content

fix(resume-checker): show grading outages to users and stop false /api/grade errors - #37

Merged
lautaropaske merged 1 commit into
mainfrom
fix/grade-gateway-unavailable
Oct 9, 2026
Merged

lautaropaske merged 1 commit into
mainfrom
fix/grade-gateway-unavailable

Conversation

@lautaropaske

@lautaropaske lautaropaske commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Four /api/grade issues from Dash0 (Oct 8–9), fixed in the route and the resume-checker page.

 /api/grade
   parseResume(pdf)
-    render every page's text            # loads fonts → "TT: undefined function" WARNs
+    read metadata only                   # grading only uses info.Author
-    pdf.js error ∉ {InvalidPDFException, Invalid PDF structure} → 500 GradingError
+    any pdf.js error → 400 InvalidPDFException
   generateObject(gemini-2.5-flash)
+    maxOutputTokens 8192, thinkingBudget 4096   # runaway generation fails fast, not at 60s
+    log { finishReason, usage } on success and on NoObjectGeneratedError
   catch
-    gateway ≥500 → 503
+    gateway 402 (budget) | 429 | ≥500 → 503 GradingUnavailable
 <ResumeChecker> (src/resume-checker/pages/index.tsx)
-  {error && <ErrorBadge error={error || mutationError} />}   # hid every API error since 0e9da65 (Jan 16)
+  <ErrorBadge error={error || mutationError} />

GradingUnavailable copy: "Perdón, no podemos analizar tu CV en este momento. Probá de nuevo en unos minutos."

Evidence

  • Budget outage (Oct 8, 16:36–16:42 UTC): GatewayInternalServerError: Team budget exceeded (402)
    Before: 500 GradingError. Users were sent back to the upload page with no message.
    After: 503 GradingUnavailable. With a mocked 503 in Chrome, the badge shows the new copy, and a fresh page load shows no badge.
  • Unreadable PDF (Oct 9, 22:33 UTC, ×3): UnknownErrorException: Command token too long: 128
    The caller is candidate-portal's hourly GradeAshbyResumesJob (infra/functions/gradeAshbyResumes.ts), which grades real candidate resumes from Ashby. The ×3 is its retry loop (MAX_GRADING_RETRIES = 3).
    Before: 500 + alert. The cron stored no grade, so the same candidate was retried every hour.
    After: 400 InvalidPDFException. The cron's InvalidPDFException branch stores its placeholder C grade ("invalid PDF") and stops retrying. That error string is part of the cron's contract, so don't rename it.
    Checked locally: a handmade PDF that throws the same error in pdf-parse, POSTed to a local dev server, returns 400 {"error":"InvalidPDFException"}.
  • Timeouts (Oct 7 ×1, Oct 9 ×3 from one IP): calls ran the full 60s while normal grades take 9–27s.
    After: output and thinking are capped. Not reproducible locally; the new usage logs will confirm the cause.
  • Tests: bun tsc clean, vitest 79/79. New cases:
    gateway 402 → 503 GradingUnavailable
    gateway 429 → 503 GradingUnavailable
    other gateway 4xx → 500 GradingError
    generateObject gets maxOutputTokens 8192 + thinkingBudget 4096
    NoObjectGeneratedError → 500, logs finishReason
    pdf.js UnknownErrorException → 400 InvalidPDFException
    parseResume renders no page text
    

Merge Danger

Door: two-way

Revert to roll back. Nothing is persisted.

Blast Radius: grading

The token caps were set without measuring production usage. If normal grades need more than 4096 thinking tokens, grades could get worse or fail with finishReason: "length". Watch the new outcome: "graded" usage logs after deploy and raise the caps if they run close. Consumers: the resume-checker page and candidate-portal's GradeAshbyResumesJob. That cron also gets the new 503 GradingUnavailable during gateway outages. It treats it like the old 500 (3 retries, then again next hour). A cap-induced 500 would leave its candidates ungraded the same way. The error badge now also shows API errors, so users may see a past error for up to 5 minutes when they return to the page.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…i/grade errors

- Answer AI Gateway 402 (budget exceeded) and 429 with 503 GradingUnavailable,
  and reword it to "Perdón, no podemos analizar tu CV en este momento".
- Render API errors in the resume-checker badge again; an `error &&` guard
  from 0e9da65 had hidden every /api/grade failure since Jan 16.
- Cap Gemini output (8192) and thinking (4096) tokens so a runaway generation
  fails fast instead of hitting the 60s timeout; log usage per grade.
- Skip page-text rendering in parseResume: grading reads only metadata, and
  font loading was the source of the "TT: undefined function" warnings.
- Answer any pdf.js parse failure with 400 InvalidPDFException, not 500.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
open-silver Ready Ready Preview Oct 9, 2026 10:51pm UTC

Request Review

@lautaropaske
lautaropaske merged commit 105abe8 into main Oct 9, 2026
3 checks passed
@lautaropaske
lautaropaske deleted the fix/grade-gateway-unavailable branch October 9, 2026 22:51

This branch was successfully deployed

1 active deployment
Preview — e265088c Deployed Oct 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant